昨天介紹了在Dart中非同步的基本概念,今天就要來講到如何簡單的控制非同步操作。
Future
可以想像成一個盒子一樣,它將 「一個值裝起來直到未來某個時間點才會打開」
直接看code,我們宣告了一個function他會回傳一個 Future<String>
,同時我也會看到 Future
最基本的 constructor Future(FutureOr<T> computation())
所以這邊我們先傳入一個會回傳String的 Function
Future<String> fetchData() => Future(
() => 'Data',
);
然後到main裡使用:
final data = fetchData();
print(data);
會發現輸出不是 Data
而是 Instance of 'Future<String>'
為什麼呢?就像前面所說的Future
可以想像成一個盒子一樣,所以我們必須將它打開才能取出它的值。
而打開這個盒子的其中一種方法就是用 then
fetchData().then((value) => print(value));
// Data
.then
會回傳一個callback 然後我們就可以用 (value) => print(value)
這種形式來使用它,那如果我的非同步是有一連串的順序呢?
假設我想要非同步的取得一個資料後,再經過三秒後才輸出的話
一樣先定義兩個回傳 Future<String>
的 function
Future<String> outputAfter3s(String data) => Future.delayed(
Duration(seconds: 3),
() => data,
);
Future<String> fetchData() => Future(
() => 'data',
);
在使用上就直接插入一個 .then
就是這麼簡單
fetchData()
.then(
(value) => outputAfter3s(value),
)
.then((value) => print(value))
整個流程大概如下:
outputAfter3s
回傳一個 Future
後,一樣是一個FutureOr<T> computation()
所以可以繼續往下接.then
,這個FutureOr
意思是可能是 T
或者 Future<T>
。
所以其實也可以直接傳一個value下去
fetchData()
.then(
(value) => outputAfter3s(value),
)
.then((value) => value)
.then((value) => print(value.length))
// 4
Future
另外一個好處是可以讓我們更方便的catch error
Future throwError() => Future(
() => throw 'error 123456',
);
// 省略其他code ...
fetchData()
.then((value) => throwError())
.then(
(value) => outputAfter3s(value),
)
.then((value) => value)
.then((value) => print(value.length))
.catchError(
(err) => print('catch error: $err'),
);
我們這邊先直接宣告一個一定會throw error的function,當然在實務上常見的可能會是http client發生一些錯誤才會throw error。
然後我們利用 catchError
來做錯誤處理,使用方式也很簡單,它一樣會回傳一個 callback 裡面可以取得這次非同步中throw error。
所以會有以下輸出
catch error: error 123456
如果我有一些操作是想要整個Future chain 都結束後且 「無論失敗或成功」 都要執行的話那我該如何寫?
fetchData()
.then((value) => throwError())
.then(
(value) => outputAfter3s(value),
)
.then((value) => value)
.then((value) => print(value.length))
.catchError(
(err) => print('catch error: $err'),
)
.whenComplete(() => print('completed'));
可以使用 whenComplete
這個api 來達成而且他是 「無論失敗或成功」 只要這個 Future
有了結果回傳都會執行。
所以輸出會是這樣
catch error: error 123456
completed
如果沒有throw error:
4
completed
所以我們現在可以得知future有三種狀態 「成功」、「失敗」、「未完成」 ,但為什麼沒有講解到 「未完成」 (pending)時候的控制呢?主要是因為這件事情有了UI才會比較需要呈現。所以等之後進入到flutter的文章時再來慢慢介紹要如何實作分別呈現 「成功」、「失敗」、「未完成」 這三種UI。
這次講了算是相當常見的 Future
,但其實我們在絕大多數的場景下很少自己建立Future
,大多數時候都是第三方服務會回傳Future
來讓我們做控制像是http request 之類的。
但你可能會想問難道我只能從 then
裡面才能將值取出來嗎?如果我有將Future
裡的值取出來放到另外一個變數該如何做?其實如果有state存在的話,是可以在Future
chain裡去將state改變。但大多數的做法應該都會是利用 async/await
來達成這件事情,就等到明天再來好好說明async/await
了。
今天的程式碼:
https://github.com/zxc469469/dart-playground/tree/Day09/future
參考資料
https://www.youtube.com/watch?v=OTS-ap9_aXc